'''
Created on 2012. 5. 21.

@author: junhee.cho
Reference :  http://gcc.gnu.org/onlinedocs/gcc/Option-Summary.html
'''

import re
import argparse
  
def flatten(listOfLists):
    "Flatten one level of nesting"
    result = []
    for elem in listOfLists:
        if isinstance(elem, list):
            result += elem
        else:
            result += [elem]
    return result

'''
OptionHandler
    kind of rules:
    1. normal-1-arg
        -o $path
        -I$dir
    2. normal-0-arg
        -c
    3. optional-value
        --help[=class,[...]]
    4. value-assign
        -std=$standard
    5. variable-assign
        -D$macro[=$defn]
        -fdisable-rtl-$pass-name[=$range-list] # default?
        -fdisable-tree-$pass-name[=$range-list] # default?
        -fenable-$kind-$pass[=$range-list] # default?
    6. optional-value
        -fdump-translation-unit[-$n]
        -femit-struct-debug-detailed[=$spec-list]
        -Wstrict-overflow[=$n] # default?
        -fprofile-use[=$path] # default?
        -mrecip[=$opt] # default?
        -mdiv[=$strategy] # default?
        -mfpu[=$name] # default?
        -msdata[=$sdata-type] # default?
        -Werror[=$specifier] # default is *
        
    7. ?
        -g[$level] # default is 2
        -fprofile-generate[=$path] # default path ?
        
'''
class OptionHandler():
    ''' 
    True if added givien option, False otherwise
    '''
    def add_argument(self, arg_parser, a_option):
        raise NotImplementedError("Should have implemented this")
    def preprocess(self, cmd_containing_option):
        raise NotImplementedError("Should have implemented this")
    
    
class OptionHandlerPositionalOptMoreThan1(OptionHandler):
    def add_argument(self, arg_parser, a_option):
        reg = r'\@([\-0-9a-zA-Z_,]+)$'
        m = re.match(reg, a_option)
        if m is None:
            return False
        arg_parser.add_argument(m.group(1), metavar=m.group(1), nargs='+')
        return True
        
    def preprocess(self, cmd):
        pass
        
class OptionHandlerRequiresOpt1(OptionHandler):
    '''
    e.g) -o $file
         -I$dir
    '''
    def add_argument(self, arg_parser, a_option):
        reg = r'(--?[\-0-9a-zA-Z_,]+)\s*\$([0-9a-zA-Z_\-]+)$'
        m = re.match(reg, a_option)
        if m is None:
            return False
        arg_parser.add_argument(m.group(1), nargs=1, metavar=m.group(2))
        return True

    def preprocess(self, cmd):
        pass
    
class OptionHandlerRequiresOpt0(OptionHandler):
    '''
    e.g) -c
    '''
    def add_argument(self, arg_parser, a_option):
        reg = r'(--?[\-\.0-9a-zA-Z_+=]+)$'
        m = re.match(reg, a_option)
        if m is None:
            return False
        arg_parser.add_argument(m.group(0), action='store_true')
        return True        

    def preprocess(self, cmd):
        pass
    
class OptionHandlerValueAssign(OptionHandler):
    '''
    e.g) -std=$standard
    '''
    reg = r'(--?[0-9a-zA-Z_\-]+)=\$([0-9a-zA-Z_\-]+)$' 
    option_reg = r'(--?[0-9a-zA-Z_\-]+)=([0-9a-zA-Z_\-]+)'   
    def add_argument(self, arg_parser, a_option):        
        m = re.match(self.reg, a_option)
        if m is None:
            return False            
        arg_parser.add_argument(m.group(1), nargs=1, metavar=m.group(2))
        return True

    def preprocess(self, cmd):
        return re.sub(self.option_reg, r'\1 \2',cmd)

            
        

class OptionHandlerVariableAssign(OptionHandler):
    '''
    e.g) -D$macro[=$defn]
    '''
    def add_argument(self, arg_parser, a_option):
        reg = r'(--?[0-9a-zA-Z_-]+)(=| )?\$([0-9a-zA-Z_\-]+)\[?=\$([0-9a-zA-Z_-]+)\]?$'
        m = re.match(reg, a_option)
        if m is None:
            return False
        arg_parser.add_argument(m.group(1), nargs=2, action='append')
        return True

    '''
    -Dmacro  => -D macro ''
    -Dmacro=v => -D macro v
    '''
    def preprocess(self, cmd):
        pass
      
class OptionHandlerMayOpt1(OptionHandler):
    '''
    e.g) -fdump-translation-unit[-$n]
         -femit-struct-debug-detailed[=$spec-list]
    '''
    def add_argument(self, arg_parser, a_option):
        reg = r'(--?[0-9a-zA-Z_-]+)\[(=|-)\$([0-9a-zA-Z_\-]+)\]$'
        m = re.match(reg, a_option)
        if m is None:
            return False
        arg_parser.add_argument(m.group(1), nargs=1)
        return True

    '''
    -Dmacro  => -D macro ''
    -Dmacro=v => -D macro v
    '''
    def preprocess(self, cmd):
        pass  
        
def get_option_handlers():
    return [OptionHandlerRequiresOpt1(), 
            OptionHandlerRequiresOpt0(),
            OptionHandlerValueAssign(),
            OptionHandlerVariableAssign(),
            OptionHandlerMayOpt1(),
            OptionHandlerPositionalOptMoreThan1()]

def add_argument(arg_parser, option):
    added = False 
    for opt_handler in get_option_handlers():
        if opt_handler.add_argument(arg_parser, option):
            added = True
            break        
    if not added:
        print "Failed : " + option
    return arg_parser
    
def is_comment(option_str):
    return option_str and option_str[0] == '#'
    
def add_arguments(arg_parser, options_file):
    f = open(options_file, 'r')
    for line in f:
        line = line.strip()
        if line and not is_comment(line) :
            arg_parser = add_argument(arg_parser, line)
            
    return arg_parser

def create_arg_parser(options_file):
    arg_parser = argparse.ArgumentParser(description='GCC arguments parser')
    arg_parser = add_arguments(arg_parser, options_file)     
    return arg_parser
        
if __name__ == "__main__":
    # ToDO: Not yet implemented
    print 'TBD'
